package top.ysez.controller.example;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author ZK-HOME-PC
 */
@Slf4j
public class Test {
  // 测试
  public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    // 数据越大线程越多
    for (int i = 0; i < 3000000; i++) {
      list.add("h--" + i);
    }
    exec(list);
  }

  public static void exec(List<String> list) {
    // 一个线程处理300条数据
    int count = 300;
    // 数据集合大小
    int listSize = list.size();
    // 开启的线程数
    int runSize = (listSize / count) + 1;
    // 创建一个线程池，数量和开启线程的数量一样
    // TODO 可替换 ExecutorService executor = Executors.newFixedThreadPool(runSize);
    ExecutorService executor =
        new ThreadPoolExecutor(
            runSize,
            runSize,
            0L,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(1024),
            new ThreadPoolExecutor.CallerRunsPolicy());
    // 创建两个个计数器
    CountDownLatch begin = new CountDownLatch(1);
    CountDownLatch end = new CountDownLatch(runSize);
    // 循环创建线程
    for (int i = 0; i < runSize; i++) {
      int startIndex;
      int endIndex;
      // 计算每个线程执行的数据
      if ((i + 1) == runSize) {
        startIndex = (i * count);
        endIndex = list.size();
      } else {
        startIndex = (i * count);
        endIndex = (i + 1) * count;
      }
      // 存放每个线程的执行数据
      List<String> subList = list.subList(startIndex, endIndex);
      // 线程类 ,这里可以用新建一个来,或者 lambda都可以
      // TODO 可替换 TestMyThread mythead = new TestMyThread(subList, begin, end);
      // 这里执行线程的方式是调用线程池里的executor.execute(mythead)方法。
      executor.execute(
          () -> {
            try {
              for (int num = 0; num < subList.size(); num++) {
                // 这里还要说一下，，由于在实质项目中，当处理的数据存在等待超时和出错会使线程一直处于等待状态
                // 这里只是处理简单的，
                // 分批 批量插入
                System.out.println(num + "-插入数据:" + subList.get(num));
              }
              // 执行完让线程直接进入等待
              begin.await();
            } catch (InterruptedException e) {
              log.error(e.getMessage(), e);
            } finally {
              // 这里要主要了，当一个线程执行完 了计数要减一不然这个线程会被一直挂起
              // ，end.countDown()，这个方法就是直接把计数器减一的
              end.countDown();
            }
          });
    }
    begin.countDown();
    try {
      end.await();
    } catch (InterruptedException e) {
      log.error(e.getMessage(), e);
    }
    // 执行完关闭线程池
    executor.shutdown();
  }
}
